//! @file   lc_delegate.h
//! @brief  
//! @author Sayan Chaliha
//! @date   September, 2011
//!
//! 
#ifndef _LC_DELEGATE_H
#define _LC_DELEGATE_H

#include "libcyan_internal.h"
#include "lc_types.h"
#include "lc_object.h"

namespace cyan {
  namespace internal {
    const Int memFuncPtrSize = sizeof (void (GenericClass::*)());

    template <Int N>
    struct CYAN_HIDDEN SimplifyMemberFunctionPointer {
      template <typename X, typename M, typename G>
      inline static GenericClass* convert(X* obj, M functionToBind,
          G& boundFunction) {
        (obj, functionToBind, boundFunction);
        typedef Char error[N - 100];
        return nullptr;
      }
    };

    template <>
    struct SimplifyMemberFunctionPointer<memFuncPtrSize> {
      template <typename X, typename M, typename G>
      inline static GenericClass* convert(X* obj, M functionToBind,
          G& boundFunction) {
        boundFunction = reinterpret_cast<G>(functionToBind);
        return reinterpret_cast<GenericClass*>(obj);
      }
    };

    template <>
    struct SimplifyMemberFunctionPointer<memFuncPtrSize + sizeof (Int)> {
      template <typename X, typename M, typename G>
      inline static GenericClass* convert(X* obj, M functionToBind,
          G& boundFunction) {
        union {
          M function;
          struct {
            G functionAddress;
            Int delta;
          } s;
        } u;
        typedef Int error[sizeof (functionToBind) == sizeof (u.s) ? 1 : -1];
        u.function = functionToBind;
        boundFunction = u.s.functionAddress;
        return reinterpret_cast<GenericClass*>(reinterpret_cast<Char*>(obj) +
            u.s.delta);
      }
    };

    struct CYAN_HIDDEN MicrosoftVirtualMemberFunctionPointer {
      void (GenericClass::*CodePointer)();
      Int delta;
      Int vtableIndex;
    };

    class CYAN_HIDDEN GenericVirtualClass : virtual public GenericClass {
    public:
      typedef GenericVirtualClass* (GenericVirtualClass::*ProbePointer)();
      GenericVirtualClass* self() { return this; }
    };

    template <>
    struct SimplifyMemberFunctionPointer<memFuncPtrSize + 2 * sizeof (Int)> {
      template <typename X, typename M, typename G>
      inline static GenericClass* convert(X* object, M functionToBind,
          G& boundFunction) {
        union {
          M function;
          GenericClass* (X::*ProbePointer)();
          MicrosoftVirtualMemberFunctionPointer s;
        } u;

        u.function = functionToBind;
        boundFunction = reinterpret_cast<G>(u.s.CodePointer);

        union {
          GenericVirtualClass::ProbePointer virtualFunction;
          MicrosoftVirtualMemberFunctionPointer s;
        } u2;

        typedef Int error[sizeof (functionToBind) == sizeof (u.s) &&
            sizeof (functionToBind) == sizeof (u.ProbePointer) &&
            sizeof (u2.virtualFunction) == sizeof (u2.s) ? 1 : -1];

        u2.virtualFunction = &GenericVirtualClass::self;
        u.s.CodePointer = u2.s.CodePointer;
        return (object->*u.ProbePointer)();
      }
    };

    template <>
    struct SimplifyMemberFunctionPointer<memFuncPtrSize + 3 * sizeof (Int)> {
      template <typename X, typename M, typename G>
      inline static GenericClass* convert(X* object, M functionToBind,
          G& boundFunction) {
        union {
          M function;
          struct {
            G functionAddress;
            Int delta;
            Int vtorDisplacement;
            Int vtableIndex;
          } s;
        } u;

        typedef Char error[sizeof (M) == sizeof (u.s) ? 1 : -1];
        u.function = functionToBind;
        boundFunction = u.s.functionAddress;

        Int virtualDelta = 0;
        if (u.s.vtableIndex != 0) {
          const Int* const vtable = reinterpret_cast<const Int* const>
              ((reinterpret_cast<const Char*>(object) + u.s.vtorDisplacement));

          virtualDelta = u.s.vtorDisplacement + *(reinterpret_cast<const Int*>
              ((reinterpret_cast<const Char*>(vtable) + u.s.vtableIndex)));
        }
        return reinterpret_cast<GenericClass*>(reinterpret_cast<Char*>
            (object) + u.s.delta + virtualDelta);
      }
    };

    class CYAN_HIDDEN DelegateMemento {
    public:
      DelegateMemento() : object_(nullptr), function_(nullptr),
          staticFunction_(nullptr) {
      }

      DelegateMemento(const DelegateMemento& other) : object_(other.object_),
          function_(other.function_), staticFunction_(other.staticFunction_) {
      }

      void clear() {
        this->object_ = nullptr;
        this->function_ = nullptr;
        this->staticFunction_ = nullptr;
      }

      inline Bool isEqual(const DelegateMemento& other) const {
        if (this->function_ != other.function_ ||
            this->staticFunction_ != other.staticFunction_)
          return false;
        else if (this->staticFunction_ != nullptr)
          return this->object_ == other.object_;
        else
          return true;
      }

      inline Bool isLess(const DelegateMemento& other) const {
        if (this->staticFunction_ != nullptr ||
            other.staticFunction_ != nullptr)
          return this->staticFunction_ < other.staticFunction_;
        else if (this->object_ != other.object_)
          return this->object_ < other.object_;

        return ::std::memcmp(&this->function_, &other.function_,
            sizeof (this->function_)) < 0;
      }

      inline Bool isEmpty() const {
        return this->function_ == nullptr && this->object_ == nullptr;
      }

      inline Bool operator !() const {
        return this->isEmpty();
      }

      DelegateMemento& operator =(const DelegateMemento& other) {
        this->function_ = other.function_;
        this->object_ = other.object_;
        this->staticFunction_ = other.staticFunction_;
        return *this;
      }

      inline Bool operator <(const DelegateMemento& rhs) const {
        return this->isLess(rhs);
      }

      inline Bool operator >(const DelegateMemento& rhs) const {
        return rhs.isLess(*this);
      }

    protected:
      typedef void (GenericClass::*GenericMemberFunction)();
      GenericClass* object_;
      GenericMemberFunction function_;

      typedef void (*GenericFunction)();
      GenericFunction staticFunction_;
    };

    template <typename G, typename S>
    class CYAN_HIDDEN Closure : public DelegateMemento {
    public:
      template <typename X, typename M>
      inline void bind(X* object, M functionToBind) {
        const Int size = sizeof (functionToBind);
        this->object_ = SimplifyMemberFunctionPointer<size>::convert(object,
            functionToBind, this->function_);
        this->staticFunction_ = nullptr;
      }

      template <typename X, typename M>
      inline void bind(const X* object, M functionToBind) {
        X* nonConstObject = const_cast<X*>(object);
        const Int size = sizeof (functionToBind);
        this->object_ = SimplifyMemberFunctionPointer<size>::convert(
            nonConstObject, functionToBind, this->function_);
        this->staticFunction_ = nullptr;
      }

      inline GenericClass* object() const {
        return this->object_;
      }

      inline G function() const {
        return reinterpret_cast<G>(this->function_);
      }

      template <typename D>
      inline void copy(D* parent, const DelegateMemento& other) {
        *(dynamic_cast<DelegateMemento*>(this)) = other;
        if (this->staticFunction_ != nullptr) {
          this->object_ = reinterpret_cast<GenericClass*>(parent);
        }
      }

      template <typename D, typename P>
      inline void bindStatic(D* parent, P staticFunctionInvoker,
          S functionToBind) {
        if (functionToBind == nullptr) {
          this->function_ = nullptr;
        } else {
          this->bind(parent, staticFunctionInvoker);
        }
        this->staticFunction_ = reinterpret_cast<GenericFunction>
            (functionToBind);
      }

      inline S staticFunction() const {
        return reinterpret_cast<S>(this->staticFunction_);
      }
    };
  }

  template <typename S>
  class Delegate;

  template <typename R>
  class CYAN_API Delegate<R ()> : public Object {
  public:
    typedef R ReturnType;
    typedef ReturnType (*StaticFunction)();
    typedef ReturnType (GenericClass::*GenericMemberFunction)();
    typedef internal::Closure<GenericMemberFunction, StaticFunction>
        ClosureType;

    Delegate() {
      this->clear();
    }

    Delegate(const Delegate& other) {
      this->closure_.copy(this, other.closure_);
    }

    template <typename X, typename Y>
    Delegate(X* object, ReturnType (Y::*memberFunction)()) {
      this->closure_.bind(implicit_cast<Y*>(object), memberFunction);
    }

    template <typename X, typename Y>
    Delegate(const X* object, ReturnType (Y::*memberFunction)()) {
      this->closure_.bind(implicit_cast<const Y*>(object), memberFunction);
    }

    Delegate(ReturnType (*staticFunction)()) {
      this->bind(staticFunction);
    }

    template <typename X, typename Y>
    inline void bind(X* object, ReturnType (Y::*memberFunction)()) {
      this->closure_.bind(implicit_cast<Y*>(object), memberFunction);
    }

    template <typename X, typename Y>
    inline void bind(const X* object, ReturnType (Y::*memberFunction)()) {
      this->closure_.bind(implicit_cast<const Y*>(object), memberFunction);
    }

    inline void bind(ReturnType (*staticFunction)()) {
      this->closure_.bindStatic(this, &Delegate::invokeStaticFunction,
          staticFunction);
    }

    inline Bool isEmpty() const {
      return !this->closure_;
    }

    inline void clear() {
      this->closure_.clear();
    }

    inline ReturnType operator ()() const {
      return ((this->closure_.object())->*(this->closure_.function()))();
    }

    inline Delegate& operator =(const Delegate& other) {
      this->closure_.copy(this, other.closure_);
      return *this;
    }

    inline Bool operator ==(const Delegate& other) const {
      return this->closure_.isEqual(other.closure_);
    }

    inline Bool operator !=(const Delegate& other) const {
      return !this->closure_.isEqual(other.closure_);
    }

    inline Bool operator <(const Delegate& other) const {
      return this->closure_.isLess(other.closure_);
    }

    inline Bool operator >(const Delegate& other) const {
      return other.closure_.isLess(this->closure_);
    }

    inline Bool operator !() const {
      return !this->closure_;
    }

    inline operator Bool() const {
      return !this->closure_.isEmpty();
    }

  private:
    ReturnType invokeStaticFunction() const {
      return (*(this->closure_.staticFunction()))();
    }

    ClosureType closure_;
  };

  template <typename R, typename P1>
  class CYAN_API Delegate<R (P1)> : public Object {
  public:
    typedef R ReturnType;
    typedef P1 Parameter1;
    typedef ReturnType (*StaticFunction)(Parameter1);
    typedef ReturnType (GenericClass::*GenericMemberFunction)(Parameter1);
    typedef internal::Closure<GenericMemberFunction, StaticFunction>
        ClosureType;

    Delegate() {
      this->clear();
    }

    Delegate(const Delegate& other) {
      this->closure_.copy(this, other.closure_);
    }

    template <typename X, typename Y>
    Delegate(X* object, ReturnType (Y::*memberFunction)(Parameter1)) {
      this->closure_.bind(implicit_cast<Y*>(object), memberFunction);
    }

    template <typename X, typename Y>
    Delegate(const X* object, ReturnType (Y::*memberFunction)(Parameter1)) {
      this->closure_.bind(implicit_cast<const Y*>(object), memberFunction);
    }

    Delegate(ReturnType (*staticFunction)(Parameter1)) {
      this->bind(staticFunction);
    }

    template <typename X, typename Y>
    inline void bind(X* object, ReturnType (Y::*memberFunction)(Parameter1)) {
      this->closure_.bind(implicit_cast<Y*>(object), memberFunction);
    }

    template <typename X, typename Y>
    inline void bind(const X* object, ReturnType
        (Y::*memberFunction)(Parameter1)) {
      this->closure_.bind(implicit_cast<const Y*>(object), memberFunction);
    }

    inline void bind(ReturnType (*staticFunction)(Parameter1)) {
      this->closure_.bindStatic(this, &Delegate::invokeStaticFunction,
          staticFunction);
    }

    inline Bool isEmpty() const {
      return !this->closure_;
    }

    inline void clear() {
      this->closure_.clear();
    }

    inline ReturnType operator ()(Parameter1 p1) const {
      return ((this->closure_.object())->*(this->closure_.function()))(p1);
    }

    inline Delegate& operator =(const Delegate& other) {
      this->closure_.copy(this, other.closure_);
      return *this;
    }

    inline Bool operator ==(const Delegate& other) const {
      return this->closure_.isEqual(other.closure_);
    }

    inline Bool operator !=(const Delegate& other) const {
      return !this->closure_.isEqual(other.closure_);
    }

    inline Bool operator <(const Delegate& other) const {
      return this->closure_.isLess(other.closure_);
    }

    inline Bool operator >(const Delegate& other) const {
      return other.closure_.isLess(this->closure_);
    }

    inline Bool operator !() const {
      return !this->closure_;
    }

    inline operator Bool() const {
      return !this->closure_.isEmpty();
    }

  private:
    ReturnType invokeStaticFunction(Parameter1 p1) const {
      return (*(this->closure_.staticFunction()))(p1);
    }

    ClosureType closure_;
  };

  template <typename R, typename P1, typename P2>
  class CYAN_API Delegate<R (P1, P2)> : public Object {
  public:
    typedef R ReturnType;
    typedef P1 Parameter1;
    typedef P2 Parameter2;
    typedef ReturnType (*StaticFunction)(Parameter1, Parameter2);
    typedef ReturnType (GenericClass::*GenericMemberFunction)(Parameter1,
        Parameter2);
    typedef internal::Closure<GenericMemberFunction, StaticFunction>
      ClosureType;

    Delegate() {
      this->clear();
    }

    Delegate(const Delegate& other) {
      this->closure_.copy(this, other.closure_);
    }

    template <typename X, typename Y>
    Delegate(X* object, ReturnType (Y::*memberFunction)(Parameter1,
        Parameter2)) {
      this->closure_.bind(implicit_cast<Y*>(object), memberFunction);
    }

    template <typename X, typename Y>
    Delegate(const X* object, ReturnType (Y::*memberFunction)(Parameter1,
        Parameter2)) {
      this->closure_.bind(implicit_cast<const Y*>(object), memberFunction);
    }

    Delegate(ReturnType (*staticFunction)(Parameter1, Parameter2)) {
      this->bind(staticFunction);
    }

    template <typename X, typename Y>
    inline void bind(X* object, ReturnType (Y::*memberFunction)(Parameter1,
        Parameter2)) {
      this->closure_.bind(implicit_cast<Y*>(object), memberFunction);
    }

    template <typename X, typename Y>
    inline void bind(const X* object, ReturnType (Y::*memberFunction)
        (Parameter1, Parameter2)) {
      this->closure_.bind(implicit_cast<const Y*>(object), memberFunction);
    }

    inline void bind(ReturnType (*staticFunction)(Parameter1, Parameter2)) {
      this->closure_.bindStatic(this, &Delegate::invokeStaticFunction,
          staticFunction);
    }

    inline Bool isEmpty() const {
      return !this->closure_;
    }

    inline void clear() {
      this->closure_.clear();
    }

    inline ReturnType operator ()(Parameter1 p1, Parameter2 p2) const {
      return ((this->closure_.object())->*(this->closure_.function()))(p1, p2);
    }

    inline Delegate& operator =(const Delegate& other) {
      this->closure_.copy(this, other.closure_);
      return *this;
    }

    inline Bool operator ==(const Delegate& other) const {
      return this->closure_.isEqual(other.closure_);
    }

    inline Bool operator !=(const Delegate& other) const {
      return !this->closure_.isEqual(other.closure_);
    }

    inline Bool operator <(const Delegate& other) const {
      return this->closure_.isLess(other.closure_);
    }

    inline Bool operator >(const Delegate& other) const {
      return other.closure_.isLess(this->closure_);
    }

    inline Bool operator !() const {
      return !this->closure_;
    }

    inline operator Bool() const {
      return !this->closure_.isEmpty();
    }

  private:
    ReturnType invokeStaticFunction(Parameter1 p1, Parameter2 p2) const {
      return (*(this->closure_.staticFunction()))(p1, p2);
    }

    ClosureType closure_;
  };

  template <typename X, typename Y, typename R>
  Delegate<R ()> CYAN_API bind(X* object, R (Y::*function)()) {
    return Delegate<R ()>(object, function);
  }

  template <typename X, typename Y, typename R>
  Delegate<R ()> CYAN_API bind(const X* object, R (Y::*function)()) {
    return Delegate<R ()>(object, function);
  }

  template <typename X, typename Y, typename R, typename P>
  Delegate<R (P)> CYAN_API bind(X* object, R (Y::*function)(P)) {
    return Delegate<R (P)>(object, function);
  }

  template <typename X, typename Y, typename R, typename P>
  Delegate<R (P)> CYAN_API bind(const X* object, R (Y::*function)(P)) {
    return Delegate<R (P)>(object, function);
  }

  template <typename X, typename Y, typename R, typename P1, typename P2>
  Delegate<R (P1, P2)> CYAN_API bind(X* object, R (Y::*function)(P1, P2)) {
    return Delegate<R (P1, P2)>(object, function);
  }

  template <typename X, typename Y, typename R, typename P1, typename P2>
  Delegate<R (P1, P2)> CYAN_API bind(const X* object, R (Y::*function)
      (P1, P2)) {
    return Delegate<R (P1, P2)>(object, function);
  }
}

#endif /* _LC_DELEGATE_H */
